from datetime import datetime, timezone

from django.utils.translation import gettext_lazy, gettext as _
from rest_framework.permissions import IsAuthenticated
from rest_framework.serializers import Serializer
from rest_framework.response import Response
from rest_framework.decorators import action
from drf_yasg.utils import swagger_auto_schema, no_body
from drf_yasg import openapi

from core import errors
from utils.lazy_funcs import lazy_str_join
from utils.paginators import NoPaginatorInspector
from apps.api.paginations import NewPageNumberPagination100
from apps.api.viewsets import NormalGenericViewSet
from apps.app_tx_meeting.managers import ReservationNoShowManager, MeetingUserWrapper, NoShowWhitelistManager
from apps.app_tx_meeting.models import ReservationNoShow
from apps.app_tx_meeting import serializers


class NoShowRestrictViewSet(NormalGenericViewSet):
    """
    违约限制时间
    """
    permission_classes = [IsAuthenticated]
    pagination_class = NewPageNumberPagination100
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('违约惩罚限制时间查询'),
        paginator_inspectors=[NoPaginatorInspector],
        responses={
            200: ''''''
        }
    )
    def list(self, request, *args, **kwargs):
        """
        违约惩罚限制时间查询

            http code 200:
            {
                "username": "zhangsan@cnic.cn",
                "restrict": true,              # 是否被违约惩罚限制
                "restrict_start": "2025-08-11T01:00:00+08:00",         # 惩罚限制开始时间, 无限制时为null
                "restrict_end": "2025-08-18T01:00:00+08:00"            # 惩罚限制结束时间, 无限制时为null
            }
        """
        meeting_user = MeetingUserWrapper(user=request.user)

        # 管理员无限制
        username = request.user.username
        restrict_start = None
        restrict_end = None
        if meeting_user.is_admin():
            restrict = False
        # 在违约白名单中，不限制
        elif NoShowWhitelistManager.is_user_in_whitelist(username):
            restrict = False
        else:
            noshow_restrict = ReservationNoShowManager.get_noshow_restrict_time(username=username)
            if noshow_restrict.end:
                restrict = True
                restrict_start = noshow_restrict.start.isoformat()
                restrict_end = noshow_restrict.end.isoformat()
            else:
                restrict = False

        return Response(
            data={
                'username': username,
                'restrict': restrict,
                'restrict_start': restrict_start,
                'restrict_end': restrict_end
            }
        )

    def get_serializer_class(self):
        return Serializer


class NoShowViewSet(NormalGenericViewSet):
    """
    违约记录
    """
    permission_classes = [IsAuthenticated]
    pagination_class = NewPageNumberPagination100
    lookup_field = 'id'

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('列举违约记录'),
        manual_parameters=[
            openapi.Parameter(
                name='time_start',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description=gettext_lazy('违约时间查询，查询区间的开始时间戳(包含此时间点，>=)。')
            ),
            openapi.Parameter(
                name='time_end',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description=gettext_lazy('违约时间查询，查询区间的结束时间戳。(不包含此时间点，<)')
            ),
            openapi.Parameter(
                name='status',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=False,
                description=lazy_str_join(gettext_lazy('违约记录状态。'), ReservationNoShow.ValidStatus.choices)
            ),
            openapi.Parameter(
                name='username',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=False,
                description=gettext_lazy('违约人账号查询，仅管理员查询有效')
            ),
        ] + NormalGenericViewSet.PARAMETERS_AS_ADMIN,
        responses={
            200: ''''''
        }
    )
    def list(self, request, *args, **kwargs):
        """
        列举违约记录

            * 查询用户个人违约记录；或者以管理员权限查询所有违约记录

            http code 200:
            {
                "count": 5,
                "page_num": 1,
                "page_size": 100,
                "results": [
                    {
                        "id": "uhsddcbjdtwhnmnxvfotxshcw",
                        "username": "tom@qq.com",   # 违约用户
                        "user_nickname": "用户1",     # 违约用户昵称
                        "creation_time": "2025-08-25T16:00:00Z",    # 违约时间
                        "valid_status": "valid",    # 违约记录有效状态；valid(有效)、invalid(无效)、豁免（豁免）
                        "email_notice_id": null,
                        "reservation": {            # 违约的会议预定记录
                            "id": "uhrmsjbz0gbwfnemqxpjxbhym",
                            "subject": "test1",
                            "start_time": 1755187200,
                            "end_time": 1755194400,
                            "room_reserve_start": 1755185400,
                            "room_reserve_end": 1755196200,
                            "people_num": 0,
                            "room_type": 50,
                            "status": "reserved",
                            "creation_time": "2025-08-20T07:26:29.995552Z",
                            "update_time": "2025-08-20T07:26:29.995652Z",
                            "user": {
                                "id": "uhpzwnf2jpbousu20unmcqcq6",
                                "username": "tom@qq.com",
                                "full_name": ""
                            },
                            "tx_start_time": 1755189000,
                            "tx_end_time": 1755196200,
                            "tx_meeting_id": "tx_meeting_id4",
                            "tx_meeting_userid": "tom@qq.com",
                            "tx_user_nick_name": "25级 丁一",
                            "tx_meeting_code": "3415363564",
                            "tx_join_url": "https://example.com1",
                            "room_reserve_status": "released",
                            "room_reserve_desc": "",
                            "advance_notice_seconds": 21600
                        }
                    }
                ]
            }
        """
        status = request.query_params.get('status')
        time_start = request.query_params.get('time_start')
        time_end = request.query_params.get('time_end')
        username = request.query_params.get('username')

        try:
            if status and status not in ReservationNoShow.ValidStatus.values:
                raise errors.InvalidArgument(message=_('无效的违约状态'))

            if time_start:
                try:
                    time_start = datetime.fromtimestamp(int(time_start), tz=timezone.utc)
                except ValueError:
                    raise errors.InvalidArgument(message=_('查询区间的开始时间戳无效'))

            if time_end:
                try:
                    time_end = datetime.fromtimestamp(int(time_end), tz=timezone.utc)
                except ValueError:
                    raise errors.InvalidArgument(message=_('查询区间的结束时间戳无效'))

            if time_start and time_end:
                if time_end <= time_start:
                    raise errors.InvalidArgument(message=_('查询区间的结束时间必须大于开始时间'))

        except errors.Error as exc:
            return self.exception_response(exc=exc)

        try:
            if self.is_as_admin_request(request=request):
                mu = MeetingUserWrapper(user=request.user)
                if not mu.is_admin():
                    raise errors.AccessDenied(message=_('你没有管理员权限'))

                qs = ReservationNoShowManager.filter_noshow_queryset(
                    time_gte=time_start, time_lt=time_end,
                    valid_status=status, username=username
                )
            else:
                qs = ReservationNoShowManager.filter_noshow_queryset(
                    time_gte=time_start, time_lt=time_end,
                    valid_status=status, username=request.user.username
                )

            qs = qs.order_by('-creation_time')
            objs = self.paginate_queryset(qs)
            serializer = self.get_serializer(instance=objs, many=True)
            return self.get_paginated_response(serializer.data)
        except Exception as exc:
            return self.exception_response(exc)

    @swagger_auto_schema(
        tags=['tx_meeting'],
        operation_summary=gettext_lazy('修改违约记录有效性'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='status',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_STRING,
                required=True,
                description=lazy_str_join(gettext_lazy('违约记录状态。'), ReservationNoShow.ValidStatus.choices)
            ),
        ],
        responses={
            200: ''''''
        }
    )
    @action(methods=['post'], detail=True, url_path='status', url_name='status')
    def change_valid_status(self, request, *args, **kwargs):
        """
        修改违约记录有效性，需要管理员权限

            http code 200:
            {
                "id": "xxx",
                "valid_status": "invalid"
            }
        """
        status = request.query_params.get('status')

        if not status:
            return self.exception_response(exc=errors.InvalidArgument(message=_('未提交要修改的违约状态')))

        if status not in ReservationNoShow.ValidStatus.values:
            return self.exception_response(exc=errors.InvalidArgument(message=_('无效的违约状态')))

        try:
            mu = MeetingUserWrapper(user=request.user)
            if not mu.is_admin():
                raise errors.AccessDenied(message=_('你没有管理员权限'))

            noshow = ReservationNoShow.objects.filter(id=kwargs[self.lookup_field]).first()
            if noshow is None:
                raise errors.TargetNotExist(message=_('违约记录不存在'))

            noshow.valid_status = status
            noshow.save(update_fields=['valid_status'])
            return Response(data={
                'id': noshow.id, 'valid_status': noshow.valid_status
            })
        except Exception as exc:
            return self.exception_response(exc)

    def get_serializer_class(self):
        if self.action == 'list':
            return serializers.NoShowSerializer

        return Serializer
